/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2011  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.graph;

import java.util.Collection;

public class Position {
  private float x = 0.0f;
  private float y = 0.0f;
  private float z = 0.0f;

  public Position() {
  }

  public Position(float x, float y, float z) {
    this.setX(x);
    this.setY(y);
    this.setZ(z);
  }

  public Position(Position position) {
    this(position.getX(), position.getY(), position.getZ());
  }

  public float getX() {
    return x;
  }

  public void setX(float x) {
    this.x = x;
  }

  public float getY() {
    return y;
  }

  public void setY(float y) {
    this.y = y;
  }

  public float getZ() {
    return z;
  }

  public void setZ(float z) {
    this.z = z;
  }

  @Override
  public boolean equals(Object object) {
    if (!(object instanceof Position)) {
      return false;
    }
    Position position = (Position) object;

    return (getX() == position.getX()
        && getY() == position.getY()
        && getZ() == position.getZ());
  }

  public void add(Position position) {
    setX(getX() + position.getX());
    setY(getY() + position.getY());
    setZ(getZ() + position.getZ());
  }

  public void subtract(Position position) {
    setX(getX() - position.getX());
    setY(getY() - position.getY());
    setZ(getZ() - position.getZ());
  }

  public void mult(Position position) {
    setX(getX() * position.getX());
    setY(getY() * position.getY());
    setZ(getZ() * position.getZ());
  }

  void div(Position position) {
    setX(getX() / position.getX());
    setY(getY() / position.getY());
    setZ(getZ() / position.getZ());
  }

  public void div(float scalar) {
    setX(getX() / scalar);
    setY(getY() / scalar);
    setZ(getZ() / scalar);
  }

  /**
   * Returns the Euclidean distance between the specified positions.
   *
   * @return Euclidean distance between the specified positions.
   */
  public float distanceTo(Position position) {
    float xDiff = this.getX() - position.getX();
    float yDiff = this.getY() - position.getY();
    float zDiff = this.getZ() - position.getZ();

    return (float) Math.sqrt(xDiff * xDiff
                             + yDiff * yDiff
                             + zDiff * zDiff);
  }

  public static Position add(Position position, float scalar) {
    Position result = new Position(position);

    result.setX(result.getX() + scalar);
    result.setY(result.getY() + scalar);
    result.setZ(result.getZ() + scalar);

    return result;
  }

  public static Position add(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.setX(result.getX() + pos2.getX());
    result.setY(result.getY() + pos2.getY());
    result.setZ(result.getZ() + pos2.getZ());

    return result;
  }

  public static Position subtract(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.setX(result.getX() - pos2.getX());
    result.setY(result.getY() - pos2.getY());
    result.setZ(result.getZ() - pos2.getZ());

    return result;
  }

  public static Position mult(Position position, float scalar) {
    Position result = new Position(position);

    result.setX(result.getX() * scalar);
    result.setY(result.getY() * scalar);
    result.setZ(result.getZ() * scalar);

    return result;
  }

  public static Position div(Position position, float scalar) {
    Position result = new Position(position);

    result.setX(result.getX() / scalar);
    result.setY(result.getY() / scalar);
    result.setZ(result.getZ() / scalar);

    return result;
  }

  public static Position min(Collection<GraphVertex> collection, boolean restrictToVisible) {
    return min(collection, restrictToVisible, true);
  }

  public static Position min(Collection<GraphVertex> collection,
      boolean restrictToVisible, boolean includeAux) {

    Position minPos = new Position(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);

    for (GraphVertex vertex : collection) {
      if ((!restrictToVisible || vertex.isShowVertex())
          && (includeAux || !vertex.isAuxiliaryEdge())) {

          minPos.setX(Math.min(minPos.getX(), vertex.getPosition().getX()));
          minPos.setY(Math.min(minPos.getY(), vertex.getPosition().getY()));
          minPos.setZ(Math.min(minPos.getZ(), vertex.getPosition().getZ()));

      }
    }
    return minPos;
  }

  public static Position max(Collection<GraphVertex> collection, boolean restrictToVisible) {
    return max(collection, restrictToVisible, true);
  }

  public static Position max(Collection<GraphVertex> collection, boolean restrictToVisible, boolean includeAux) {
    Position maxPos = new Position(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
    for (GraphVertex vertex : collection) {
      if ((!restrictToVisible || vertex.isShowVertex())
          && (includeAux || !vertex.isAuxiliaryEdge())) {

        maxPos.setX(Math.max(maxPos.getX(), vertex.getPosition().getX()));
        maxPos.setY(Math.max(maxPos.getY(), vertex.getPosition().getY()));
        maxPos.setZ(Math.max(maxPos.getZ(), vertex.getPosition().getZ()));
      }
    }
    return maxPos;
  }

  /**
   * Returns the maximum over each dimension of the width of two points.
   *
   * @return Maximum over each dimension of the width of two points.
   */
  public static float width(Position pos1, Position pos2) {
    Position diffPos = Position.subtract(pos1, pos2);
    float width = Math.max(diffPos.getX(), diffPos.getY());
    width = Math.max(diffPos.getZ(), width);

    return width;
  }
}
